/* vcc/plugins/m4/m4.c 
 * 
 * This file is part of vcc. 
 * 
 * vcc is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version. 
 * 
 * vcc is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with vcc. If not, see <https://www.gnu.org/licenses/>
 */ 




#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>

#include <vcc/vcc.h>
#include <vcc/pretty.h>
#include <vcc/plugin.h>
#include <vcc/interfaces.h>
#include <vcc/version.h>


int m4_init(void);
int do_cmd_m4def(int, char **);

char *m4cachep;
int m4cache_size;

struct cmdtable m4_cmdtab[] = {
	{ "-m4def", do_cmd_m4def, "Define a macro which i will remember. " }, 
	{ NULL, NULL, NULL }
};

struct plugin plg_m4 = {
	.state = 0, 
	.name = "m4", 
	.descr = "GNU M4 Expanding. ", 
	.init = m4_init, 
	/* the hook will remove by plugins.c */
	.cmdtab = m4_cmdtab
};

#define this_plg (&plg_m4)

int do_cmd_m4def(int argc, char **argv) {
	char 	buf[MSG_SIZE];
	int 	size, i = 0, t;

	(void) 	argv;

	plg_check_enabled();

	if (argc != 1) {
		fprintf(stderr, _("usage: -m4def\n"));

		return 1;
	}

	memset(buf, 0, MSG_SIZE);

	for (i = 0; i < (int) MSG_SIZE; i += t) {
		fprintf(stderr, "m4> ");
		t = read(0, buf + i, MSG_SIZE);

		if (!strncmp(buf + i, "finish", 6)) 
			break;

		size = t;

		/* remember it */
		if (unlikely(!(m4cachep = realloc(m4cachep, m4cache_size + size)))) {
			fprintf(stderr, "*** malloc() failed\n");

			return 1;
		}

		memset(m4cachep + m4cache_size, 0, size);

		strcat(m4cachep, buf + i);
		m4cache_size += size;
	}

	return 0;
}


static inline int check_hist_enabled(void) {
	if (find_plg("hist")->state == PLG_INSERTED) 
		return 1;

	return 0;
}


/* this causes a infinite recursion, which is the thing m4 programmers are afraid of.  */

int m4_write_predef(char *buf) {
	(void) buf;
	
#if 0
	struct history 		*hist;
	char 			*p;
	char 			s[128];

	if (!check_hist_enabled()) {
		fprintf(stderr, _("the history plugin (hist) doesn't insert so m4 preder can't work. \n"));

		return 0;
	}

	hist = hist_getmsg(1);

	if (!hist) 
		p = "\n";
	else 
		p = hist->msg;

	sprintf(s, "divert(-1)define(`LAST_MSG', `%s'\n)divert(0)dnl\n", p);
	strcat(buf, s);

#endif
	return 0;
}


char *m4_hook(struct hook_info *hi) {
	int 	fdt;
	FILE 	*fp;
	char 	*p;

	if (unlikely((fdt = open("/tmp/.vcc-tmp", O_WRONLY | O_TRUNC | O_CREAT | O_APPEND, 0666)) < 0)) {
		perror("open");

		return NULL;
	}

	if (unlikely(!(p = malloc(8192)))) {
		fprintf(stderr, "*** malloc() failed\n");

		return NULL;
	}

	memset(p, 0, 8192);
	
	if (m4cachep) 
		strcat(p, m4cachep);

	m4_write_predef(p);
	strcat(p, hi->msg);

	write(fdt, p, strlen(p));
	close(fdt);

	if (unlikely(!(fp = popen("/bin/m4 /tmp/.vcc-tmp", "r")))) {
		perror("popen");

		return NULL;
	}

	if (unlikely(!(p = malloc(MSG_SIZE)))) {
		fprintf(stderr, "*** malloc() failed\n");

		return NULL;
	}

	memset(p, 0, MSG_SIZE);
	fread(p, 1, MSG_SIZE, fp);

	return p;
}


int m4_init(void) {
	m4cachep = NULL;
	m4cache_size = 0;

#if 0
	if (check_hist_enabled()) {
		fprintf(stderr, "*** fatal: enable m4 after the hist enabled will cause a RECURSION when try to use LAST_MSG! \n");

		return 1;
	}
#endif

	register_hook(HOOK_SEND, m4_hook);
	return 0;
}


